﻿using System;
using System.Collections.Generic;
using ValidationFramework.Reflection;
using System.Linq;
using System.Collections.ObjectModel;

namespace ValidationFramework.Web.MVC
{
	/// <summary>
	/// Will extract all client-side enabled rules
	/// </summary>
	/// <remarks>By default, rules will be placed</remarks>
	public class TypeExtractionStrategy : IRuleExtractionStrategy
	{
		private bool _isUsingWhiteList;
		private TypePolicy _typePolicy;

		public ICollection<string> WhiteList {get; set;}
		public ICollection<string> BlackList { get; set; }

		/// <summary>
		/// Initializes a new instance of the <see cref="TypeExtractionStrategy"/> class.
		/// </summary>
		public TypeExtractionStrategy(TypePolicy typePolicy)
		{
			_typePolicy = typePolicy;
			this.BlackList = new Collection<string>();
			this.WhiteList = new Collection<string>();
		}


		#region IRuleExtractionStrategy Members

		public IList<RuleInfo> GetRules()
		{
			var rules = new List<RuleInfo>();
			foreach (var kvp in _typePolicy.PropertyPolicyDictionary)
			{
				var policy = kvp.Value;
				if (!ShouldIncludeRulesFor(policy.MemberName))
				{
                    continue; // skip this property 
                }

				// only extract member rules
				foreach (var rule in policy.MemberScopeRules)
				{
					rules.Add(new RuleInfo(policy.MemberName, rule));
				}
			}
			return rules;
		}

		#endregion

		public void AddToWhiteList(string propertyName)
		{
			if (this.BlackList.Count > 0)
				throw new InvalidOperationException("Can't add to white list when black list already has items. The white list and black list can't be used at the same time");

			this.WhiteList.Add(propertyName);
			_isUsingWhiteList = true;
		}
		public void AddToBlackList(string propertyName)
		{
			if (this.WhiteList.Count > 0)
				throw new InvalidOperationException("Can't add to black list when white list already has items. The white list and black list can't be used at the same time");

			this.BlackList.Add(propertyName);
			_isUsingWhiteList = false;
		}

		/// <summary>
		/// Exposed for testing
		/// </summary>
		internal bool ShouldIncludeRulesFor(string name)
		{
			if (WhiteList.Count <= 0 && BlackList.Count <= 0)
				return true; // by default all properties are allowed

			if (_isUsingWhiteList)
				return WhiteList.Contains(name); // include rule if in whitelist
			else
				return !BlackList.Contains(name); // don't include rule if contained in black list
		}
	}
}
